/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package biofilter.filters;

import biofilter.exceptions.InvalidRecordException;
import biofilter.records.FileNameRecord;
import biofilter.records.Record;
import biofilter.util.ProcessHandler;
import biofilter.util.ProcessUtil;
import biofilter.util.SystemProperties;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * This Filter runs blast.  It is a prototype and will need to be extended over time
 * @author Daniel Quest
 * @date   Feb 4 2010
 */
public class BlastFilter extends SimpleFilter {
    /**
     * blastall 2.2.18   arguments:

  -p  Program Name [String]
  -d  Database [String]
    default = nr
  -i  Query File [File In]
    default = stdin
  -e  Expectation value (E) [Real]
    default = 10.0
  -m  alignment view options:
0 = pairwise,
1 = query-anchored showing identities,
2 = query-anchored no identities,
3 = flat query-anchored, show identities,
4 = flat query-anchored, no identities,
5 = query-anchored no identities and blunt ends,
6 = flat query-anchored, no identities and blunt ends,
7 = XML Blast output,
8 = tabular, 
9 tabular with comment lines
10 ASN, text
11 ASN, binary [Integer]
    default = 0
    range from 0 to 11
  -o  BLAST report Output File [File Out]  Optional
    default = stdout
  -F  Filter query sequence (DUST with blastn, SEG with others) [String]
    default = T
  -G  Cost to open a gap (-1 invokes default behavior) [Integer]
    default = -1
  -E  Cost to extend a gap (-1 invokes default behavior) [Integer]
    default = -1
  -X  X dropoff value for gapped alignment (in bits) (zero invokes default behavior)
      blastn 30, megablast 20, tblastx 0, all others 15 [Integer]
    default = 0
  -I  Show GI's in deflines [T/F]
    default = F
  -q  Penalty for a nucleotide mismatch (blastn only) [Integer]
    default = -3
  -r  Reward for a nucleotide match (blastn only) [Integer]
    default = 1
  -v  Number of database sequences to show one-line descriptions for (V) [Integer]
    default = 500
  -b  Number of database sequence to show alignments for (B) [Integer]
    default = 250
  -f  Threshold for extending hits, default if zero
      blastp 11, blastn 0, blastx 12, tblastn 13
      tblastx 13, megablast 0 [Real]
    default = 0
  -g  Perform gapped alignment (not available with tblastx) [T/F]
    default = T
  -Q  Query Genetic code to use [Integer]
    default = 1
  -D  DB Genetic code (for tblast[nx] only) [Integer]
    default = 1
  -a  Number of processors to use [Integer]
    default = 1
  -O  SeqAlign file [File Out]  Optional
  -J  Believe the query defline [T/F]
    default = F
  -M  Matrix [String]
    default = BLOSUM62
  -W  Word size, default if zero (blastn 11, megablast 28, all others 3) [Integer]
    default = 0
  -z  Effective length of the database (use zero for the real size) [Real]
    default = 0
  -K  Number of best hits from a region to keep. Off by default.
If used a value of 100 is recommended.  Very high values of -v or -b is also suggested [Integer]
    default = 0
  -P  0 for multiple hit, 1 for single hit (does not apply to blastn) [Integer]
    default = 0
  -Y  Effective length of the search space (use zero for the real size) [Real]
    default = 0
  -S  Query strands to search against database (for blast[nx], and tblastx)
       3 is both, 1 is top, 2 is bottom [Integer]
    default = 3
  -T  Produce HTML output [T/F]
    default = F
  -l  Restrict search of database to list of GI's [String]  Optional
  -U  Use lower case filtering of FASTA sequence [T/F]  Optional
  -y  X dropoff value for ungapped extensions in bits (0.0 invokes default behavior)
      blastn 20, megablast 10, all others 7 [Real]
    default = 0.0
  -Z  X dropoff value for final gapped alignment in bits (0.0 invokes default behavior)
      blastn/megablast 50, tblastx 0, all others 25 [Integer]
    default = 0
  -R  PSI-TBLASTN checkpoint file [File In]  Optional
  -n  MegaBlast search [T/F]
    default = F
  -L  Location on query sequence [String]  Optional
  -A  Multiple Hits window size, default if zero (blastn/megablast 0, all others 40 [Integer]
    default = 0
  -w  Frame shift penalty (OOF algorithm for blastx) [Integer]
    default = 0
  -t  Length of the largest intron allowed in a translated nucleotide sequence when linking multiple distinct alignments. (0 invokes default behavior; a negative value disables linking.) [Integer]
    default = 0
  -B  Number of concatenated queries, for blastn and tblastn [Integer]  Optional
    default = 0
  -V  Force use of the legacy BLAST engine [T/F]  Optional
    default = F
  -C  Use composition-based score adjustments for blastp or tblastn:
      As first character:
      D or d: default (equivalent to T)
      0 or F or f: no composition-based statistics
      2 or T or t: Composition-based score adjustments as in Bioinformatics 21:902-911,
      1: Composition-based statistics as in NAR 29:2994-3005, 2001
          2005, conditioned on sequence properties
      3: Composition-based score adjustment as in Bioinformatics 21:902-911,
          2005, unconditionally
      For programs other than tblastn, must either be absent or be D, F or 0.
           As second character, if first character is equivalent to 1, 2, or 3:
      U or u: unified p-value combining alignment p-value and compositional p-value in round 1 only
 [String]
    default = D
  -s  Compute locally optimal Smith-Waterman alignments (This option is only
      available for gapped tblastn.) [T/F]
    default = F

     */
    
    private SystemProperties sysprop = null;
    private ProcessUtil pu = new ProcessUtil();
    private String tempDir = "/tmp";
    public BlastFilter(){
        super();
        this.options = " -a 4 -m 8 -e 0.0001";
        init();
    }

    public BlastFilter(String options){
        super();
        this.options = options;
        init();
    }
    private String options = null;
    public void setOptions(String o) {
        this.options = o;
    }
    private String blastLoc = "blastall";
    public void setBlastEXE(String blastLocationPath){
        this.blastLoc = blastLocationPath;
    }
    private String database = null;
    public void setDatabase(String db){
        this.database = db;
    }
    private String hitFileName = null;
    public void setHitFileName(String hitFileName) {
        if (hitFileName == null) pu.getUniqueFileName(".blast");
        else this.hitFileName = hitFileName;
    }
    private String blastdir = null; //the directory where we are suposed to place the result files
    private String blastProgram = null;
    private void init(){
        try {
            sysprop = new SystemProperties();
            setTmp( sysprop.get("tmp") );
            setBlastEXE( sysprop.get("bowtie"));
            this.blastdir = this.tempDir;
            this.blastProgram = "-p blastn";
        } catch (IOException ex) {
            Logger.getLogger(BowtieFilter.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    /**
     * usage: programName = blastn or blastp
     * @param programName
     */
    public void setBlastProgram(String programName){
        this.blastProgram = "-p " + programName;
    }

    public void setTmp(String tempDir){
        this.tempDir = tempDir;
        if (!this.tempDir.endsWith("/")){
            this.tempDir = tempDir + "/";
        }
    }
    public void setBlastDir(String dirPath){
        this.blastdir = dirPath;
    }

    @Override
    protected Record transformRecord(Record r) throws InvalidRecordException {
        StringBuffer stdOut = null;
        StringBuffer stdErr = null;

        try {
            this.transformedRecord = null;
            if (!(r instanceof FileNameRecord)) {
                throw new RuntimeException("type mismatch, expected FileNameRecord");
            }if(r == null){
                throw new RuntimeException("FileNameRecord is null!");
            }if(hitFileName == null){
                this.setHitFileName(null);
            }

            stdOut = new StringBuffer();
            stdErr = new StringBuffer();

            //blastall -a 4 -m 8 -p blastn -e 0.0001 -i ./testReads.fa -d /auto/transcriptomics/data/genomes/Bacteria/Clostridium_thermocellum_ATCC_27405/NC_009012 -o ./test.blast
            String command = this.blastLoc + " " + options + " -d" + this.database + " -i" + r.get() + " -o" + this.blastdir + hitFileName;

            Logger.getLogger(BowtieFilter.class.getName()).log(Level.INFO, "Running command: " + command);
            Logger.getLogger(BowtieFilter.class.getName()).log(Level.INFO, "Bowtie File created: " + this.blastdir + hitFileName);
            int pid = ProcessHandler.runCommand(command, stdOut, stdErr);

        } catch (IOException ex) {
            Logger.getLogger(BowtieFilter.class.getName()).log(Level.SEVERE, null, ex);
            throw new InvalidRecordException(ex.getMessage());
        } catch (InterruptedException ex) {
            Logger.getLogger(BowtieFilter.class.getName()).log(Level.SEVERE, null, ex);
            throw new InvalidRecordException(ex.getMessage());
        }

        return (new FileNameRecord(this.blastdir + hitFileName));
    }

    public Record peek() throws InvalidRecordException {
        throw new UnsupportedOperationException("Not supported yet.");
    }
}
